package com.tally.framework.security.handler;

import com.tally.common.utils.IpUtils;

import com.tally.common.utils.JsonUtils;
import com.tally.framework.security.dto.LoginUser;

import com.tally.framework.security.utils.SecurityUtils;
import com.tally.framework.web.AjaxResult;
import com.tally.project.system.user.domain.User;
import com.tally.project.system.user.domain.vo.UserVo;
import com.tally.project.tool.token.dto.Token;
import com.tally.project.tool.token.service.TokenService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.session.SessionInformation;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.session.InvalidSessionStrategy;
import org.springframework.security.web.session.SessionInformationExpiredEvent;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * spring security处理器
 */
@Configuration
public class SecurityHandler {

	private Logger logger = LoggerFactory.getLogger(getClass());

    @Resource
    private TokenService tokenService;

	/**
	 * 登录成功处理
	 * @return
	 */
	@Bean
	public AuthenticationSuccessHandler loginSuccessHandler() {
		return new AuthenticationSuccessHandler() {
			@Override
			public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
				// 获取登录用户信息
				LoginUser loginUser = (LoginUser) authentication.getPrincipal();
				// 删除Redis缓存旧用户信息
                tokenService.deleteToken(loginUser.getUsername());
                // 保存token
                Token token = tokenService.saveToken(loginUser, IpUtils.getIpAddr(request));
                logger.info("登录认证成功");
				// 这里写你登录成功后的逻辑，可以验证其他信息，如验证码等。
				response.setContentType("application/json;charset=UTF-8");
				// 重定向
				User user = SecurityUtils.getUser();
				user.setToken(token.getToken());
				response.getWriter().println(JsonUtils.objectToJson(AjaxResult.success("登录成功", new UserVo(user))));
			}
		};
	}

	/**
	 * 登陆失败处理
	 * @return
	 */
	@Bean
	public AuthenticationFailureHandler loginFailureHandler() {
		return new AuthenticationFailureHandler() {
			@Override
			public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {
				logger.info("登录失败");
				String msg = null;
				if (exception instanceof BadCredentialsException) {
					msg = "密码错误";
				} else {
					msg = exception.getMessage();
				}
				// 这里写你登录失败后的逻辑，可以验证其他信息，如验证码等。
				response.setContentType("application/json;charset=UTF-8");
				// 重定向
				response.getWriter().println(JsonUtils.objectToJson(AjaxResult.error(msg)));
			}
		};
	}

	/**
	 * 未登录处理
	 * @return
	 */
	@Bean
	public AuthenticationEntryPoint authenticationEntryPoint() {
		return new AuthenticationEntryPoint() {
			@Override
			public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
				// 这里写你登录失败后的逻辑，可以验证其他信息，如验证码等。
				response.setContentType("application/json;charset=UTF-8");
				// 重定向
				response.getWriter().println(JsonUtils.objectToJson(new AjaxResult(AjaxResult.Type.NOT_LOGIN,"尚未登录，请登录!")));
			}
		};
	}

	/**
	 * 注销登录处理
	 * @return
	 */
	@Bean
	public LogoutSuccessHandler logoutSussHandler() {
		return new LogoutSuccessHandler() {
			@Override
			public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
                logger.info("注销成功!");
                LoginUser loginUser = (LoginUser) authentication.getPrincipal();
                // 删除缓存中用户信息
                tokenService.deleteToken(loginUser.getUsername());
				response.setContentType("application/json;charset=UTF-8");
				response.getWriter().println(JsonUtils.objectToJson(AjaxResult.success("注销成功!")));
			}
		};
	}

    /**
     * 会话信息过期处理器
     * @return
     */
    @Bean
    public InvalidSessionStrategy onInvalidSessionDetected() {
        return new InvalidSessionStrategy () {
            @Override
            public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
                logger.info("会话信息过期处理器!");
                response.setContentType("application/json;charset=UTF-8");
                response.getWriter().println(JsonUtils.objectToJson(new AjaxResult(AjaxResult.Type.WARN,"当前登录已失效！请重新登录")));
            }
        };
    }

    /**
     * 会话信息结束处理器
     * @return
     */
    @Bean
    public SessionInformationExpiredStrategy onExpiredSessionDetected() {
        return new SessionInformationExpiredStrategy() {
            @Override
            public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException {
                logger.info("会话信息结束处理器!");
                // 获取过期会话信息
                SessionInformation sessionInformation = (SessionInformation) event.getSource();
                // 获取过期用户信息
                LoginUser loginUser = (LoginUser) sessionInformation.getPrincipal();
                // 删除缓存中Token
                tokenService.deleteToken(loginUser.getUsername());
                event.getResponse().setContentType("application/json;charset=UTF-8");
                event.getResponse().getWriter().println(JsonUtils.objectToJson(new AjaxResult(AjaxResult.Type.SESSION_EXPIRED,"您的账号已在其他设备登陆")));
            }
        };
    }

}
